package com.zxj.judge.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zxj.api.RemoteJudgeService;
import com.zxj.api.domain.UserExeResult;
import com.zxj.api.domain.dto.JudgeSubmitDTO;
import com.zxj.api.domain.vo.UserQuestionResultVO;
import com.zxj.common.core.contains.JudgeConstants;
import com.zxj.common.core.enums.CodeRunStatus;
import com.zxj.judge.domain.SandBoxExecuteResult;
import com.zxj.judge.domain.UserSubmit;
import com.zxj.judge.mapper.UserSubmitMapper;
import com.zxj.judge.service.IJudgeService;
import com.zxj.judge.service.ISandboxPoolService;
import com.zxj.judge.service.ISandboxService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zxj.common.core.contains.Constants;

import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class JudgeServiceImpl implements IJudgeService {

    @Autowired
    private ISandboxService sandboxService;

    @Autowired
    private ISandboxPoolService sandboxPoolService;

    @Autowired
    private UserSubmitMapper userSubmitMapper;



    /**
     * Java版代码判题逻辑
     * @param judgeSubmitDTO 判题提交 DTO
     * @return 用户提交题目判题结果
     */
    @Override
    public UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {
        log.info("---- 判题逻辑开始 -------");
        // 1. 调用沙箱服务执行用户代码
        SandBoxExecuteResult sandBoxExecuteResult =
                sandboxPoolService.exeJavaCode(judgeSubmitDTO.getUserId(), judgeSubmitDTO.getUserCode(), judgeSubmitDTO.getInputList());
        // 2. 判断用户代码执行结果
        UserQuestionResultVO userQuestionResultVO = new UserQuestionResultVO();
        if (sandBoxExecuteResult != null && CodeRunStatus.SUCCEED.equals(sandBoxExecuteResult.getRunStatus())) { // 执行成功
            //比对直接结果  时间限制  空间限制的比对
            userQuestionResultVO = doJudge(judgeSubmitDTO, sandBoxExecuteResult, userQuestionResultVO);
        } else { // 执行失败, 设置错误信息
            userQuestionResultVO.setPass(Constants.FALSE);
            if (sandBoxExecuteResult != null) { // 沙箱执行结果不为空
                userQuestionResultVO.setExeMessage(sandBoxExecuteResult.getExeMessage());
            } else { // 沙箱执行结果为空
                userQuestionResultVO.setExeMessage(CodeRunStatus.UNKNOWN_FAILED.getMsg());
            }
            // 设置分数为0
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
        }
        // 3. 保存用户提交结果
        saveUserSubmit(judgeSubmitDTO, userQuestionResultVO);
        log.info("判题逻辑结束，判题结果为： {} ", userQuestionResultVO);
        // 返回用户提交题目判题结果
        return userQuestionResultVO;
    }

    /**
     * 判断运行结果是否正确
     * @param judgeSubmitDTO 判题提交 DTO
     * @param sandBoxExecuteResult 沙箱执行结果
     * @param userQuestionResultVO 用户提交题目判题结果
     * @return 用户提交题目判题结果
     */
    private UserQuestionResultVO doJudge(JudgeSubmitDTO judgeSubmitDTO,
                                         SandBoxExecuteResult sandBoxExecuteResult,
                                         UserQuestionResultVO userQuestionResultVO) {
        // 1. 比对用户输出和期望输出 数量是否一致
        List<String> exeOutputList = sandBoxExecuteResult.getOutputList(); // 实际输出
        List<String> outputList = judgeSubmitDTO.getOutputList(); // 期望输出
        if (outputList.size() != exeOutputList.size()) { // 输出数量不一致
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE); // 设置分数为0
            userQuestionResultVO.setPass(Constants.FALSE); // 设置未通过
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg()); // 设置错误信息
            return userQuestionResultVO; // 返回用户提交题目判题结果
        }
        // 2. 比对用户输出和期望输出 内容是否一致
        List<UserExeResult> userExeResultList = new ArrayList<>();
        boolean passed = resultCompare(judgeSubmitDTO, exeOutputList, outputList, userExeResultList); // 比对结果
        return assembleUserQuestionResultVO(judgeSubmitDTO, sandBoxExecuteResult, userQuestionResultVO, userExeResultList, passed);
    }

    /**
     * 组装用户提交题目判题结果
     * @param judgeSubmitDTO 判题提交 DTO
     * @param sandBoxExecuteResult 沙箱执行结果
     * @param userQuestionResultVO 用户提交题目判题结果
     * @param userExeResultList 用户执行结果列表
     * @param passed 是否通过
     * @return 用户提交题目判题结果
     */
    private UserQuestionResultVO assembleUserQuestionResultVO(JudgeSubmitDTO judgeSubmitDTO,
                                                              SandBoxExecuteResult sandBoxExecuteResult,
                                                              UserQuestionResultVO userQuestionResultVO,
                                                              List<UserExeResult> userExeResultList, boolean passed) {
        // 设置用户执行结果列表
        userQuestionResultVO.setUserExeResultList(userExeResultList);

        // 存在用例未通过
        if (!passed) {
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }

        // 超出内存限制
        if (sandBoxExecuteResult.getUseMemory() > judgeSubmitDTO.getSpaceLimit()) {
            log.debug("内存超出限制，实际内存：{}， 限制内存：{}", sandBoxExecuteResult.getUseMemory(), judgeSubmitDTO.getSpaceLimit());
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_MEMORY.getMsg());
            return userQuestionResultVO;
        }

        // 超出时间限制
        if (sandBoxExecuteResult.getUseTime() > judgeSubmitDTO.getTimeLimit()) {
            log.debug("时间超出限制，实际时间：{}， 限制时间：{}", sandBoxExecuteResult.getUseTime(), judgeSubmitDTO.getTimeLimit());
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_TIME.getMsg());
            return userQuestionResultVO;
        }

        // 设置通过
        userQuestionResultVO.setPass(Constants.TRUE);
        int score = judgeSubmitDTO.getDifficulty() * JudgeConstants.DEFAULT_SCORE; // 设置分数
        userQuestionResultVO.setScore(score);
        return userQuestionResultVO;
    }

    /**
     * 期望输出与实际输出数量正确, 比对两者内容是否一致
     * @param judgeSubmitDTO 判题提交 DTO
     * @param exeOutputList 实际输出
     * @param outputList 期望输出
     * @param userExeResultList 用户执行结果, 用于记录用户输入, 期望输出, 实际输出
     * @return 是否通过
     */
    private boolean resultCompare(JudgeSubmitDTO judgeSubmitDTO, List<String> exeOutputList,
                                  List<String> outputList, List<UserExeResult> userExeResultList) {
        boolean passed = true;
        for (int index = 0; index < outputList.size(); index++) { // 比对每个用例
            // 获取期望输出, 实际输出, 用户输入
            String output = outputList.get(index);
            String exeOutPut = exeOutputList.get(index);
            String input = judgeSubmitDTO.getInputList().get(index);
            // 记录用户输入, 期望输出, 实际输出
            UserExeResult userExeResult = new UserExeResult();
            userExeResult.setInput(input);
            userExeResult.setOutput(output);
            userExeResult.setExeOutput(exeOutPut);
            userExeResultList.add(userExeResult);
            if (!output.equals(exeOutPut)) { // 期望输出与实际输出不一致
                passed = false;
                log.info("输入：{}， 期望输出：{}， 实际输出：{} ", input, output, exeOutputList);
            }
        }
        return passed;
    }

    /**
     * 数据库保存用户提交结果
     * @param judgeSubmitDTO 判题提交 DTO
     * @param userQuestionResultVO 用户提交题目判题结果
     */
    private void saveUserSubmit(JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO) {
        UserSubmit userSubmit = new UserSubmit();
        BeanUtil.copyProperties(userQuestionResultVO, userSubmit);
        userSubmit.setUserId(judgeSubmitDTO.getUserId());
        userSubmit.setQuestionId(judgeSubmitDTO.getQuestionId());
        userSubmit.setExamId(judgeSubmitDTO.getExamId());
        userSubmit.setProgramType(judgeSubmitDTO.getProgramType());
        userSubmit.setUserCode(judgeSubmitDTO.getUserCode());
        userSubmit.setCaseJudgeRes(JSON.toJSONString(userQuestionResultVO.getUserExeResultList()));
        userSubmit.setCreateBy(judgeSubmitDTO.getUserId());
        userSubmit.setUpdateBy(judgeSubmitDTO.getUserId());
        // 保存用户最新一次提交记录
        userSubmitMapper.delete(new LambdaQueryWrapper<UserSubmit>()
                .eq(UserSubmit::getUserId, judgeSubmitDTO.getUserId())
                .eq(UserSubmit::getQuestionId, judgeSubmitDTO.getQuestionId())
                .isNull(judgeSubmitDTO.getExamId() == null, UserSubmit::getExamId)
                .eq(judgeSubmitDTO.getExamId() != null, UserSubmit::getExamId, judgeSubmitDTO.getExamId()));
        userSubmitMapper.insert(userSubmit);
    }
}
